/*
Copyright (C) 2018-2019 de4dot@gmail.com

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#if GAS
using System;
using System.Diagnostics;
using Iced.Intel.FormatterInternal;

namespace Iced.Intel.GasFormatterInternal {
	// GENERATOR-BEGIN: InstrOpKind
	// ⚠️This was generated by GENERATOR!🦹‍♂️
	enum InstrOpKind : byte {
		Register,
		NearBranch16,
		NearBranch32,
		NearBranch64,
		FarBranch16,
		FarBranch32,
		Immediate8,
		Immediate8_2nd,
		Immediate16,
		Immediate32,
		Immediate64,
		Immediate8to16,
		Immediate8to32,
		Immediate8to64,
		Immediate32to64,
		MemorySegSI,
		MemorySegESI,
		MemorySegRSI,
		MemorySegDI,
		MemorySegEDI,
		MemorySegRDI,
		MemoryESDI,
		MemoryESEDI,
		MemoryESRDI,
		Memory64,
		Memory,
		Sae,
		RnSae,
		RdSae,
		RuSae,
		RzSae,
		DeclareByte,
		DeclareWord,
		DeclareDword,
		DeclareQword,
	}
	// GENERATOR-END: InstrOpKind

	struct InstrOpInfo {
		internal const int TEST_RegisterBits = IcedConstants.RegisterBits;

		public FormatterString Mnemonic;
		public InstrOpInfoFlags Flags;
		public byte OpCount;
		public InstrOpKind Op0Kind;
		public InstrOpKind Op1Kind;
		public InstrOpKind Op2Kind;
		public InstrOpKind Op3Kind;
		public InstrOpKind Op4Kind;
		public byte Op0Register;
		public byte Op1Register;
		public byte Op2Register;
		public byte Op3Register;
		public byte Op4Register;
		public sbyte Op0Index;
		public sbyte Op1Index;
		public sbyte Op2Index;
		public sbyte Op3Index;
		public sbyte Op4Index;

		public readonly int GetOpRegister(int operand) =>
			operand switch {
				0 => Op0Register,
				1 => Op1Register,
				2 => Op2Register,
				3 => Op3Register,
				4 => Op4Register,
				_ => throw new ArgumentOutOfRangeException(nameof(operand)),
			};

		public readonly InstrOpKind GetOpKind(int operand) {
			switch (operand) {
			case 0: return Op0Kind;
			case 1: return Op1Kind;
			case 2: return Op2Kind;
			case 3: return Op3Kind;
			case 4: return Op4Kind;
			default:
				Debug.Assert(Op0Kind == InstrOpKind.DeclareByte || Op0Kind == InstrOpKind.DeclareWord || Op0Kind == InstrOpKind.DeclareDword || Op0Kind == InstrOpKind.DeclareQword);
				return Op0Kind;
			}
		}

		public readonly int GetInstructionIndex(int operand) {
			int instructionOperand;
			switch (operand) {
			case 0: instructionOperand = Op0Index; break;
			case 1: instructionOperand = Op1Index; break;
			case 2: instructionOperand = Op2Index; break;
			case 3: instructionOperand = Op3Index; break;
			case 4: instructionOperand = Op4Index; break;
			default:
				Debug.Assert(Op0Kind == InstrOpKind.DeclareByte || Op0Kind == InstrOpKind.DeclareWord || Op0Kind == InstrOpKind.DeclareDword || Op0Kind == InstrOpKind.DeclareQword);
				instructionOperand = -1;
				break;
			}
			return instructionOperand < 0 ? -1 : instructionOperand;
		}

#if INSTR_INFO
		public readonly bool TryGetOpAccess(int operand, out OpAccess access) {
			int instructionOperand;
			switch (operand) {
			case 0: instructionOperand = Op0Index; break;
			case 1: instructionOperand = Op1Index; break;
			case 2: instructionOperand = Op2Index; break;
			case 3: instructionOperand = Op3Index; break;
			case 4: instructionOperand = Op4Index; break;
			default:
				Debug.Assert(Op0Kind == InstrOpKind.DeclareByte || Op0Kind == InstrOpKind.DeclareWord || Op0Kind == InstrOpKind.DeclareDword || Op0Kind == InstrOpKind.DeclareQword);
				instructionOperand = Op0Index;
				break;
			}
			if (instructionOperand < InstrInfo.OpAccess_INVALID) {
				access = (OpAccess)(-instructionOperand - 2);
				return true;
			}
			access = OpAccess.None;
			return false;
		}
#endif

		public readonly int GetOperandIndex(int instructionOperand) {
			int index;
			if (instructionOperand == Op0Index)
				index = 0;
			else if (instructionOperand == Op1Index)
				index = 1;
			else if (instructionOperand == Op2Index)
				index = 2;
			else if (instructionOperand == Op3Index)
				index = 3;
			else if (instructionOperand == Op4Index)
				index = 4;
			else
				index = -1;
			return index < OpCount ? index : -1;
		}

		public InstrOpInfo(FormatterString mnemonic, in Instruction instruction, InstrOpInfoFlags flags) {
			Static.Assert(IcedConstants.MaxOpCount == 5 ? 0 : -1);
			Mnemonic = mnemonic;
			Flags = flags;
			int opCount = instruction.OpCount;
			OpCount = (byte)opCount;
			if ((flags & InstrOpInfoFlags.KeepOperandOrder) != 0) {
				Op0Kind = (InstrOpKind)instruction.Op0Kind;
				Op1Kind = (InstrOpKind)instruction.Op1Kind;
				Op2Kind = (InstrOpKind)instruction.Op2Kind;
				Op3Kind = (InstrOpKind)instruction.Op3Kind;
				Op4Kind = (InstrOpKind)instruction.Op4Kind;
				Op0Register = (byte)instruction.Op0Register;
				Op1Register = (byte)instruction.Op1Register;
				Op2Register = (byte)instruction.Op2Register;
				Op3Register = (byte)instruction.Op3Register;
				Op4Register = (byte)instruction.Op4Register;
			}
			else {
				switch (opCount) {
				case 0:
					Op0Kind = 0;
					Op1Kind = 0;
					Op2Kind = 0;
					Op3Kind = 0;
					Op4Kind = 0;
					Op0Register = 0;
					Op1Register = 0;
					Op2Register = 0;
					Op3Register = 0;
					Op4Register = 0;
					break;

				case 1:
					Op0Kind = (InstrOpKind)instruction.Op0Kind;
					Op1Kind = 0;
					Op2Kind = 0;
					Op3Kind = 0;
					Op4Kind = 0;
					Op0Register = (byte)instruction.Op0Register;
					Op1Register = 0;
					Op2Register = 0;
					Op3Register = 0;
					Op4Register = 0;
					break;

				case 2:
					Op0Kind = (InstrOpKind)instruction.Op1Kind;
					Op1Kind = (InstrOpKind)instruction.Op0Kind;
					Op2Kind = 0;
					Op3Kind = 0;
					Op4Kind = 0;
					Op0Register = (byte)instruction.Op1Register;
					Op1Register = (byte)instruction.Op0Register;
					Op2Register = 0;
					Op3Register = 0;
					Op4Register = 0;
					break;

				case 3:
					Op0Kind = (InstrOpKind)instruction.Op2Kind;
					Op1Kind = (InstrOpKind)instruction.Op1Kind;
					Op2Kind = (InstrOpKind)instruction.Op0Kind;
					Op3Kind = 0;
					Op4Kind = 0;
					Op0Register = (byte)instruction.Op2Register;
					Op1Register = (byte)instruction.Op1Register;
					Op2Register = (byte)instruction.Op0Register;
					Op3Register = 0;
					Op4Register = 0;
					break;

				case 4:
					Op0Kind = (InstrOpKind)instruction.Op3Kind;
					Op1Kind = (InstrOpKind)instruction.Op2Kind;
					Op2Kind = (InstrOpKind)instruction.Op1Kind;
					Op3Kind = (InstrOpKind)instruction.Op0Kind;
					Op4Kind = 0;
					Op0Register = (byte)instruction.Op3Register;
					Op1Register = (byte)instruction.Op2Register;
					Op2Register = (byte)instruction.Op1Register;
					Op3Register = (byte)instruction.Op0Register;
					Op4Register = 0;
					break;

				case 5:
					Op0Kind = (InstrOpKind)instruction.Op4Kind;
					Op1Kind = (InstrOpKind)instruction.Op3Kind;
					Op2Kind = (InstrOpKind)instruction.Op2Kind;
					Op3Kind = (InstrOpKind)instruction.Op1Kind;
					Op4Kind = (InstrOpKind)instruction.Op0Kind;
					Op0Register = (byte)instruction.Op4Register;
					Op1Register = (byte)instruction.Op3Register;
					Op2Register = (byte)instruction.Op2Register;
					Op3Register = (byte)instruction.Op1Register;
					Op4Register = (byte)instruction.Op0Register;
					break;

				default:
					throw new InvalidOperationException();
				}
			}
			switch (OpCount) {
			case 0:
				Op0Index = InstrInfo.OpAccess_INVALID;
				Op1Index = InstrInfo.OpAccess_INVALID;
				Op2Index = InstrInfo.OpAccess_INVALID;
				Op3Index = InstrInfo.OpAccess_INVALID;
				Op4Index = InstrInfo.OpAccess_INVALID;
				break;

			case 1:
				Op0Index = 0;
				Op1Index = InstrInfo.OpAccess_INVALID;
				Op2Index = InstrInfo.OpAccess_INVALID;
				Op3Index = InstrInfo.OpAccess_INVALID;
				Op4Index = InstrInfo.OpAccess_INVALID;
				break;

			case 2:
				Op0Index = 1;
				Op1Index = 0;
				Op2Index = InstrInfo.OpAccess_INVALID;
				Op3Index = InstrInfo.OpAccess_INVALID;
				Op4Index = InstrInfo.OpAccess_INVALID;
				break;

			case 3:
				Op0Index = 2;
				Op1Index = 1;
				Op2Index = 0;
				Op3Index = InstrInfo.OpAccess_INVALID;
				Op4Index = InstrInfo.OpAccess_INVALID;
				break;

			case 4:
				Op0Index = 3;
				Op1Index = 2;
				Op2Index = 1;
				Op3Index = 0;
				Op4Index = InstrInfo.OpAccess_INVALID;
				break;

			case 5:
				Op0Index = 4;
				Op1Index = 3;
				Op2Index = 2;
				Op3Index = 1;
				Op4Index = 0;
				break;

			default:
				throw new InvalidOperationException();
			}
		}
	}

	abstract class InstrInfo {
		public const int OpAccess_INVALID = -1;
#if INSTR_INFO
		public const int OpAccess_None = -(int)(OpAccess.None + 2);
		public const int OpAccess_Read = -(int)(OpAccess.Read + 2);
		public const int OpAccess_CondRead = -(int)(OpAccess.CondRead + 2);
		public const int OpAccess_Write = -(int)(OpAccess.Write + 2);
		public const int OpAccess_CondWrite = -(int)(OpAccess.CondWrite + 2);
		public const int OpAccess_ReadWrite = -(int)(OpAccess.ReadWrite + 2);
		public const int OpAccess_ReadCondWrite = -(int)(OpAccess.ReadCondWrite + 2);
		public const int OpAccess_NoMemAccess = -(int)(OpAccess.NoMemAccess + 2);
#else
		public const int OpAccess_None = OpAccess_INVALID;
		public const int OpAccess_Read = OpAccess_INVALID;
		public const int OpAccess_CondRead = OpAccess_INVALID;
		public const int OpAccess_Write = OpAccess_INVALID;
		public const int OpAccess_CondWrite = OpAccess_INVALID;
		public const int OpAccess_ReadWrite = OpAccess_INVALID;
		public const int OpAccess_ReadCondWrite = OpAccess_INVALID;
		public const int OpAccess_NoMemAccess = OpAccess_INVALID;
#endif

		public abstract void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info);

		protected static int GetBitness(CodeSize codeSize) =>
			codeSize switch {
				CodeSize.Code16 => 16,
				CodeSize.Code32 => 32,
				CodeSize.Code64 => 64,
				_ => 0,
			};

		protected static FormatterString GetMnemonic(FormatterOptions options, in Instruction instruction, FormatterString mnemonic, FormatterString mnemonic_suffix, InstrOpInfoFlags flags) {
			if (options.GasShowMnemonicSizeSuffix)
				return mnemonic_suffix;
			if ((flags & InstrOpInfoFlags.MnemonicSuffixIfMem) != 0 && MemorySizes.AllMemorySizes[(int)instruction.MemorySize].IsDefault) {
				OpKind opKind;
				if ((opKind = instruction.Op0Kind) == OpKind.Memory || opKind == OpKind.Memory64 ||
					(opKind = instruction.Op1Kind) == OpKind.Memory || opKind == OpKind.Memory64 ||
					instruction.Op2Kind == OpKind.Memory)
					return mnemonic_suffix;
			}
			return mnemonic;
		}
	}

	sealed class SimpleInstrInfo : InstrInfo {
		readonly FormatterString mnemonic;
		readonly FormatterString mnemonic_suffix;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo(string mnemonic) : this(mnemonic, mnemonic, InstrOpInfoFlags.None) { }
		public SimpleInstrInfo(string mnemonic, InstrOpInfoFlags flags) : this(mnemonic, mnemonic, flags) { }
		public SimpleInstrInfo(string mnemonic, string mnemonic_suffix) : this(mnemonic, mnemonic_suffix, InstrOpInfoFlags.None) { }

		public SimpleInstrInfo(string mnemonic, string mnemonic_suffix, InstrOpInfoFlags flags) {
			this.mnemonic = new FormatterString(mnemonic);
			this.mnemonic_suffix = new FormatterString(mnemonic_suffix);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) =>
			info = new InstrOpInfo(GetMnemonic(options, instruction, mnemonic, mnemonic_suffix, flags), instruction, flags);
	}

	sealed class SimpleInstrInfo_cc : InstrInfo {
		readonly int ccIndex;
		readonly FormatterString[] mnemonics;
		readonly FormatterString[] mnemonics_suffix;

		public SimpleInstrInfo_cc(int ccIndex, string[] mnemonics, string[] mnemonics_suffix) {
			this.ccIndex = ccIndex;
			this.mnemonics = FormatterString.Create(mnemonics);
			this.mnemonics_suffix = FormatterString.Create(mnemonics_suffix);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			const InstrOpInfoFlags flags = InstrOpInfoFlags.None;
			var mnemonic = MnemonicCC.GetMnemonicCC(options, ccIndex, mnemonics);
			var mnemonic_suffix = MnemonicCC.GetMnemonicCC(options, ccIndex, mnemonics_suffix);
			info = new InstrOpInfo(GetMnemonic(options, instruction, mnemonic, mnemonic_suffix, flags), instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_AamAad : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_AamAad(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			if (instruction.Immediate8 == 10) {
				info = default;
				info.Mnemonic = mnemonic;
			}
			else
				info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
		}
	}

	sealed class SimpleInstrInfo_nop : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;
		readonly Register register;

		public SimpleInstrInfo_nop(int bitness, string mnemonic, Register register) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
			this.register = register;
		}

		static readonly FormatterString str_xchg = new FormatterString("xchg");
		static readonly FormatterString str_xchgw = new FormatterString("xchgw");
		static readonly FormatterString str_xchgl = new FormatterString("xchgl");
		static readonly FormatterString str_xchgq = new FormatterString("xchgq");

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			int instrBitness = GetBitness(instruction.CodeSize);
			if (instrBitness == 0 || (instrBitness & bitness) != 0)
				info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
			else {
				info = default;
				if (!options.GasShowMnemonicSizeSuffix)
					info.Mnemonic = str_xchg;
				else if (register == Register.AX)
					info.Mnemonic = str_xchgw;
				else if (register == Register.EAX)
					info.Mnemonic = str_xchgl;
				else if (register == Register.RAX)
					info.Mnemonic = str_xchgq;
				else
					throw new InvalidOperationException();
				info.OpCount = 2;
				Static.Assert(InstrOpKind.Register == 0 ? 0 : -1);
				//info.Op0Kind = InstrOpKind.Register;
				//info.Op1Kind = InstrOpKind.Register;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)register;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op1Register = (byte)register;
				info.Op0Index = OpAccess_None;
				info.Op1Index = OpAccess_None;
			}
		}
	}

	sealed class SimpleInstrInfo_STIG1 : InstrInfo {
		readonly FormatterString mnemonic;
		readonly bool pseudoOp;

		public SimpleInstrInfo_STIG1(string mnemonic) : this(mnemonic, false) { }

		public SimpleInstrInfo_STIG1(string mnemonic, bool pseudoOp) {
			this.mnemonic = new FormatterString(mnemonic);
			this.pseudoOp = pseudoOp;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = default;
			info.Mnemonic = mnemonic;
			Debug.Assert(instruction.OpCount == 2);
			Debug.Assert(instruction.Op0Kind == OpKind.Register && instruction.Op0Register == Register.ST0);
			if (!pseudoOp || !(options.UsePseudoOps && instruction.Op1Register == Register.ST1)) {
				info.OpCount = 1;
				Static.Assert(InstrOpKind.Register == 0 ? 0 : -1);
				//info.Op0Kind = InstrOpKind.Register;
				info.Op0Register = (byte)instruction.Op1Register;
				info.Op0Index = 1;
			}
		}
	}

	sealed class SimpleInstrInfo_STi_ST2 : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_STi_ST2(string mnemonic) {
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			const InstrOpInfoFlags flags = 0;
			if (options.UsePseudoOps && (instruction.Op0Register == Register.ST1 || instruction.Op1Register == Register.ST1)) {
				info = default;
				info.Mnemonic = mnemonic;
			}
			else {
				info = new InstrOpInfo(mnemonic, instruction, flags);
				Debug.Assert(info.Op0Register == (int)Register.ST0);
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)Registers.Register_ST;
			}
		}
	}

	sealed class SimpleInstrInfo_ST_STi : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_ST_STi(string mnemonic) =>
			this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
			Debug.Assert(info.Op1Register == (int)Register.ST0);
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op1Register = (byte)Registers.Register_ST;
		}
	}

	sealed class SimpleInstrInfo_STi_ST : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_STi_ST(string mnemonic) =>
			this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
			Debug.Assert(info.Op0Register == (int)Register.ST0);
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op0Register = (byte)Registers.Register_ST;
		}
	}

	sealed class SimpleInstrInfo_as : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_as(int bitness, string mnemonic) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			InstrOpInfoFlags flags = 0;
			var instrBitness = GetBitness(instruction.CodeSize);
			if (instrBitness != 0 && instrBitness != bitness) {
				if (bitness == 16)
					flags |= InstrOpInfoFlags.AddrSize16;
				else if (bitness == 32)
					flags |= InstrOpInfoFlags.AddrSize32;
				else
					flags |= InstrOpInfoFlags.AddrSize64;
			}
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_maskmovq : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_maskmovq(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			Debug.Assert(instruction.OpCount == 3);

			var instrBitness = GetBitness(instruction.CodeSize);

			int bitness = instruction.Op0Kind switch {
				OpKind.MemorySegDI => 16,
				OpKind.MemorySegEDI => 32,
				OpKind.MemorySegRDI => 64,
				_ => instrBitness,
			};

			info = default;
			info.Mnemonic = mnemonic;
			info.OpCount = 2;
			info.Op0Kind = (InstrOpKind)instruction.Op2Kind;
			info.Op0Register = (byte)instruction.Op2Register;
			info.Op0Index = 2;
			info.Op1Kind = (InstrOpKind)instruction.Op1Kind;
			info.Op1Register = (byte)instruction.Op1Register;
			info.Op1Index = 1;
			if (instrBitness != 0 && instrBitness != bitness) {
				if (bitness == 16)
					info.Flags |= InstrOpInfoFlags.AddrSize16;
				else if (bitness == 32)
					info.Flags |= InstrOpInfoFlags.AddrSize32;
				else
					info.Flags |= InstrOpInfoFlags.AddrSize64;
			}
		}
	}

	sealed class SimpleInstrInfo_pblendvb : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_pblendvb(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = default;
			Debug.Assert(instruction.OpCount == 2);
			info.Mnemonic = mnemonic;
			info.OpCount = 3;
			Static.Assert(InstrOpKind.Register == 0 ? 0 : -1);
			//info.Op0Kind = InstrOpKind.Register;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op0Register = (byte)Register.XMM0;
			info.Op0Index = OpAccess_Read;
			info.Op1Kind = (InstrOpKind)instruction.Op1Kind;
			info.Op1Index = 1;
			info.Op1Register = (byte)instruction.Op1Register;
			info.Op2Kind = (InstrOpKind)instruction.Op0Kind;
			info.Op2Register = (byte)instruction.Op0Register;
		}
	}

	sealed class SimpleInstrInfo_OpSize : InstrInfo {
		readonly CodeSize codeSize;
		readonly FormatterString[] mnemonics;

		public SimpleInstrInfo_OpSize(CodeSize codeSize, string mnemonic, string mnemonic16, string mnemonic32, string mnemonic64) {
			this.codeSize = codeSize;
			mnemonics = new FormatterString[4];
			mnemonics[(int)CodeSize.Unknown] = new FormatterString(mnemonic);
			mnemonics[(int)CodeSize.Code16] = new FormatterString(mnemonic16);
			mnemonics[(int)CodeSize.Code32] = new FormatterString(mnemonic32);
			mnemonics[(int)CodeSize.Code64] = new FormatterString(mnemonic64);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			FormatterString mnemonic;
			if (instruction.CodeSize == codeSize && !options.GasShowMnemonicSizeSuffix)
				mnemonic = mnemonics[(int)CodeSize.Unknown];
			else
				mnemonic = mnemonics[(int)codeSize];
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
		}
	}

	sealed class SimpleInstrInfo_OpSize2_bnd : InstrInfo {
		readonly FormatterString[] mnemonics;

		public SimpleInstrInfo_OpSize2_bnd(string mnemonic, string mnemonic16, string mnemonic32, string mnemonic64) {
			mnemonics = new FormatterString[4];
			mnemonics[(int)CodeSize.Unknown] = new FormatterString(mnemonic);
			mnemonics[(int)CodeSize.Code16] = new FormatterString(mnemonic16);
			mnemonics[(int)CodeSize.Code32] = new FormatterString(mnemonic32);
			mnemonics[(int)CodeSize.Code64] = new FormatterString(mnemonic64);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			if (instruction.HasRepnePrefix)
				flags |= InstrOpInfoFlags.BndPrefix;
			int codeSize = (int)instruction.CodeSize;
			if (options.GasShowMnemonicSizeSuffix)
				codeSize = (int)CodeSize.Code64;
			var mnemonic = mnemonics[codeSize];
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_OpSize3 : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;
		readonly FormatterString mnemonic_suffix;

		public SimpleInstrInfo_OpSize3(int bitness, string mnemonic, string mnemonic_suffix) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
			this.mnemonic_suffix = new FormatterString(mnemonic_suffix);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var instrBitness = GetBitness(instruction.CodeSize);
			FormatterString mnemonic;
			if (!options.GasShowMnemonicSizeSuffix && (instrBitness == 0 || (instrBitness & bitness) != 0))
				mnemonic = this.mnemonic;
			else
				mnemonic = mnemonic_suffix;
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
		}
	}

	sealed class SimpleInstrInfo_os : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_os(int bitness, string mnemonic) : this(bitness, mnemonic, InstrOpInfoFlags.None) { }
		public SimpleInstrInfo_os(int bitness, string mnemonic, InstrOpInfoFlags flags) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = this.flags;
			int instrBitness = GetBitness(instruction.CodeSize);
			if (instrBitness != 0 && instrBitness != bitness) {
				if (bitness == 16)
					flags |= InstrOpInfoFlags.OpSize16;
				else if (bitness == 32)
					flags |= InstrOpInfoFlags.OpSize32;
				else
					flags |= InstrOpInfoFlags.OpSize64;
			}
			info = new InstrOpInfo(GetMnemonic(options, instruction, mnemonic, mnemonic, flags), instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_os2 : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;
		readonly FormatterString mnemonic_suffix;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_os2(int bitness, string mnemonic, string mnemonic_suffix) : this(bitness, mnemonic, mnemonic_suffix, InstrOpInfoFlags.None) { }

		public SimpleInstrInfo_os2(int bitness, string mnemonic, string mnemonic_suffix, InstrOpInfoFlags flags) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
			this.mnemonic_suffix = new FormatterString(mnemonic_suffix);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			FormatterString mnemonic;
			int instrBitness = GetBitness(instruction.CodeSize);
			if (instrBitness != 0 && instrBitness != bitness)
				mnemonic = mnemonic_suffix;
			else
				mnemonic = GetMnemonic(options, instruction, this.mnemonic, mnemonic_suffix, flags);
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_os2_bnd : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;
		readonly FormatterString mnemonic_suffix;

		public SimpleInstrInfo_os2_bnd(int bitness, string mnemonic, string mnemonic_suffix) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
			this.mnemonic_suffix = new FormatterString(mnemonic_suffix);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			if (instruction.HasRepnePrefix)
				flags |= InstrOpInfoFlags.BndPrefix;
			FormatterString mnemonic;
			int instrBitness = GetBitness(instruction.CodeSize);
			if (instrBitness != 0 && instrBitness != bitness)
				mnemonic = mnemonic_suffix;
			else
				mnemonic = GetMnemonic(options, instruction, this.mnemonic, mnemonic_suffix, flags);
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_os_bnd : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_os_bnd(int bitness, string mnemonic) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			if (instruction.HasRepnePrefix)
				flags |= InstrOpInfoFlags.BndPrefix;
			int instrBitness = GetBitness(instruction.CodeSize);
			if (instrBitness != 0 && instrBitness != bitness) {
				if (bitness == 16)
					flags |= InstrOpInfoFlags.OpSize16;
				else if (bitness == 32)
					flags |= InstrOpInfoFlags.OpSize32;
				else
					flags |= InstrOpInfoFlags.OpSize64;
			}
			info = new InstrOpInfo(GetMnemonic(options, instruction, mnemonic, mnemonic, flags), instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_os_mem : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;
		readonly FormatterString mnemonic_suffix;

		public SimpleInstrInfo_os_mem(int bitness, string mnemonic, string mnemonic_suffix) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
			this.mnemonic_suffix = new FormatterString(mnemonic_suffix);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			int instrBitness = GetBitness(instruction.CodeSize);
			bool hasMemOp = instruction.Op0Kind == OpKind.Memory || instruction.Op1Kind == OpKind.Memory;
			if (hasMemOp && !(instrBitness == 0 || (instrBitness != 64 && instrBitness == bitness) || (instrBitness == 64 && bitness == 32))) {
				if (bitness == 16)
					flags |= InstrOpInfoFlags.OpSize16;
				else if (bitness == 32)
					flags |= InstrOpInfoFlags.OpSize32;
				else
					flags |= InstrOpInfoFlags.OpSize64;
			}
			var mnemonic = hasMemOp ? this.mnemonic : GetMnemonic(options, instruction, this.mnemonic, mnemonic_suffix, flags);
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_os_mem2 : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;
		readonly FormatterString mnemonic_suffix;

		public SimpleInstrInfo_os_mem2(int bitness, string mnemonic, string mnemonic_suffix) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
			this.mnemonic_suffix = new FormatterString(mnemonic_suffix);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			int instrBitness = GetBitness(instruction.CodeSize);
			FormatterString mnemonic;
			if ((instrBitness != 0 && (instrBitness & bitness) == 0))
				mnemonic = mnemonic_suffix;
			else
				mnemonic = this.mnemonic;
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
		}
	}

	sealed class SimpleInstrInfo_os_mem_reg16 : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_os_mem_reg16(int bitness, string mnemonic) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			int instrBitness = GetBitness(instruction.CodeSize);
			Debug.Assert(instruction.OpCount == 1);
			if (instruction.Op0Kind == OpKind.Memory) {
				if (!(instrBitness == 0 || (instrBitness != 64 && instrBitness == bitness) || (instrBitness == 64 && bitness == 32))) {
					if (bitness == 16)
						flags |= InstrOpInfoFlags.OpSize16;
					else if (bitness == 32)
						flags |= InstrOpInfoFlags.OpSize32;
					else
						flags |= InstrOpInfoFlags.OpSize64;
				}
			}
			info = new InstrOpInfo(mnemonic, instruction, flags);
			if (instruction.Op0Kind == OpKind.Register) {
				var reg = (Register)info.Op0Register;
				int regSize = 0;
				if (Register.AX <= reg && reg <= Register.R15W)
					regSize = 16;
				else if (Register.EAX <= reg && reg <= Register.R15D) {
					regSize = 32;
					reg = reg - Register.EAX + Register.AX;
				}
				else if (Register.RAX <= reg && reg <= Register.R15) {
					regSize = 64;
					reg = reg - Register.RAX + Register.AX;
				}
				Debug.Assert(regSize != 0);
				if (regSize != 0) {
					Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
					info.Op0Register = (byte)reg;
					if (!((instrBitness != 64 && instrBitness == regSize) || (instrBitness == 64 && regSize == 32))) {
						if (bitness == 16)
							info.Flags |= InstrOpInfoFlags.OpSize16;
						else if (bitness == 32)
							info.Flags |= InstrOpInfoFlags.OpSize32;
						else
							info.Flags |= InstrOpInfoFlags.OpSize64;
					}
				}
			}
		}
	}

	sealed class SimpleInstrInfo_os_loop : InstrInfo {
		readonly int bitness;
		readonly int regSize;
		readonly int ccIndex;
		readonly FormatterString[] mnemonics;
		readonly FormatterString[] mnemonics_suffix;

		public SimpleInstrInfo_os_loop(int bitness, int regSize, int ccIndex, string[] mnemonics, string[] mnemonics_suffix) {
			this.bitness = bitness;
			this.regSize = regSize;
			this.ccIndex = ccIndex;
			this.mnemonics = FormatterString.Create(mnemonics);
			this.mnemonics_suffix = FormatterString.Create(mnemonics_suffix);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			int instrBitness = GetBitness(instruction.CodeSize);
			var mnemonics = this.mnemonics;
			if ((instrBitness != 0 && instrBitness != regSize) || options.GasShowMnemonicSizeSuffix)
				mnemonics = mnemonics_suffix;
			if (instrBitness != 0 && instrBitness != bitness) {
				if (bitness == 16)
					flags |= InstrOpInfoFlags.OpSize16 | InstrOpInfoFlags.OpSizeIsByteDirective;
				else if (bitness == 32)
					flags |= InstrOpInfoFlags.OpSize32 | InstrOpInfoFlags.OpSizeIsByteDirective;
				else
					flags |= InstrOpInfoFlags.OpSize64;
			}
			var mnemonic = ccIndex == -1 ? mnemonics[0] : MnemonicCC.GetMnemonicCC(options, ccIndex, mnemonics);
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_os_jcc : InstrInfo {
		readonly int bitness;
		readonly int ccIndex;
		readonly FormatterString[] mnemonics;

		public SimpleInstrInfo_os_jcc(int bitness, int ccIndex, string[] mnemonics) {
			this.bitness = bitness;
			this.ccIndex = ccIndex;
			this.mnemonics = FormatterString.Create(mnemonics);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			int instrBitness = GetBitness(instruction.CodeSize);
			if (instrBitness != 0 && instrBitness != bitness) {
				if (bitness == 16)
					flags |= InstrOpInfoFlags.OpSize16;
				else if (bitness == 32)
					flags |= InstrOpInfoFlags.OpSize32;
				else
					flags |= InstrOpInfoFlags.OpSize64;
			}
			var prefixSeg = instruction.SegmentPrefix;
			if (prefixSeg == Register.CS)
				flags |= InstrOpInfoFlags.JccNotTaken;
			else if (prefixSeg == Register.DS)
				flags |= InstrOpInfoFlags.JccTaken;
			if (instruction.HasRepnePrefix)
				flags |= InstrOpInfoFlags.BndPrefix;
			var mnemonic = MnemonicCC.GetMnemonicCC(options, ccIndex, mnemonics);
			info = new InstrOpInfo(GetMnemonic(options, instruction, mnemonic, mnemonic, flags), instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_movabs : InstrInfo {
		readonly int memOpNumber;
		readonly FormatterString mnemonic;
		readonly FormatterString mnemonic_suffix;
		readonly FormatterString mnemonic64;
		readonly FormatterString mnemonic_suffix64;

		public SimpleInstrInfo_movabs(int memOpNumber, string mnemonic, string mnemonic_suffix, string mnemonic64, string mnemonic_suffix64) {
			this.memOpNumber = memOpNumber;
			this.mnemonic = new FormatterString(mnemonic);
			this.mnemonic_suffix = new FormatterString(mnemonic_suffix);
			this.mnemonic64 = new FormatterString(mnemonic64);
			this.mnemonic_suffix64 = new FormatterString(mnemonic_suffix64);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			int instrBitness = GetBitness(instruction.CodeSize);
			var opKind = instruction.GetOpKind(memOpNumber);
			int memSize;
			FormatterString mnemonic, mnemonic_suffix;
			if (opKind == OpKind.Memory64) {
				mnemonic = mnemonic64;
				mnemonic_suffix = mnemonic_suffix64;
				memSize = 64;
			}
			else {
				mnemonic = this.mnemonic;
				mnemonic_suffix = this.mnemonic_suffix;
				Debug.Assert(opKind == OpKind.Memory);
				int displSize = instruction.MemoryDisplSize;
				memSize = displSize == 2 ? 16 : 32;
			}
			if (instrBitness == 0)
				instrBitness = memSize;
			if (instrBitness == 64) {
				if (memSize == 32)
					flags |= InstrOpInfoFlags.AddrSize32;
			}
			else if (instrBitness != memSize) {
				Debug.Assert(memSize == 16 || memSize == 32);
				if (memSize == 16)
					flags |= InstrOpInfoFlags.AddrSize16;
				else
					flags |= InstrOpInfoFlags.AddrSize32;
			}
			info = new InstrOpInfo(GetMnemonic(options, instruction, mnemonic, mnemonic_suffix, flags), instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_er : InstrInfo {
		readonly int erIndex;
		readonly FormatterString mnemonic;
		readonly FormatterString mnemonic_suffix;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_er(int erIndex, string mnemonic) : this(erIndex, mnemonic, mnemonic, InstrOpInfoFlags.None) { }

		public SimpleInstrInfo_er(int erIndex, string mnemonic, string mnemonic_suffix, InstrOpInfoFlags flags) {
			this.erIndex = erIndex;
			this.mnemonic = new FormatterString(mnemonic);
			this.mnemonic_suffix = new FormatterString(mnemonic_suffix);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(GetMnemonic(options, instruction, mnemonic, mnemonic_suffix, flags), instruction, flags);
			var rc = instruction.RoundingControl;
			if (rc != RoundingControl.None) {
				if (!FormatterUtils.CanShowRoundingControl(instruction, options))
					return;
				InstrOpKind rcOpKind;
				switch (rc) {
				case RoundingControl.RoundToNearest:	rcOpKind = InstrOpKind.RnSae; break;
				case RoundingControl.RoundDown:			rcOpKind = InstrOpKind.RdSae; break;
				case RoundingControl.RoundUp:			rcOpKind = InstrOpKind.RuSae; break;
				case RoundingControl.RoundTowardZero:	rcOpKind = InstrOpKind.RzSae; break;
				default:
					return;
				}
				MoveOperands(ref info, erIndex, rcOpKind);
			}
		}

		internal static void MoveOperands(ref InstrOpInfo info, int index, InstrOpKind newOpKind) {
			Debug.Assert(info.OpCount <= 4);

			switch (index) {
			case 0:
				info.Op4Kind = info.Op3Kind;
				info.Op4Register = info.Op3Register;
				info.Op3Kind = info.Op2Kind;
				info.Op3Register = info.Op2Register;
				info.Op2Kind = info.Op1Kind;
				info.Op2Register = info.Op1Register;
				info.Op1Kind = info.Op0Kind;
				info.Op1Register = info.Op0Register;
				info.Op0Kind = newOpKind;
				info.Op4Index = info.Op3Index;
				info.Op3Index = info.Op2Index;
				info.Op2Index = info.Op1Index;
				info.Op1Index = info.Op0Index;
				info.Op0Index = OpAccess_None;
				info.OpCount++;
				break;

			case 1:
				info.Op4Kind = info.Op3Kind;
				info.Op4Register = info.Op3Register;
				info.Op3Kind = info.Op2Kind;
				info.Op3Register = info.Op2Register;
				info.Op2Kind = info.Op1Kind;
				info.Op2Register = info.Op1Register;
				info.Op1Kind = newOpKind;
				info.Op4Index = info.Op3Index;
				info.Op3Index = info.Op2Index;
				info.Op2Index = info.Op1Index;
				info.Op1Index = OpAccess_None;
				info.OpCount++;
				break;

			default:
				throw new InvalidOperationException();
			}
		}
	}

	sealed class SimpleInstrInfo_sae : InstrInfo {
		readonly int saeIndex;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_sae(int saeIndex, string mnemonic) {
			this.saeIndex = saeIndex;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
			if (instruction.SuppressAllExceptions)
				SimpleInstrInfo_er.MoveOperands(ref info, saeIndex, InstrOpKind.Sae);
		}
	}

	sealed class SimpleInstrInfo_far : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;
		readonly FormatterString mnemonic_suffix;

		public SimpleInstrInfo_far(int bitness, string mnemonic, string mnemonic_suffix) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
			this.mnemonic_suffix = new FormatterString(mnemonic_suffix);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.IndirectOperand;
			var instrBitness = GetBitness(instruction.CodeSize);
			FormatterString mnemonic;
			if (instrBitness == 0)
				instrBitness = bitness;
			if (bitness == 64) {
				flags |= InstrOpInfoFlags.OpSize64;
				Debug.Assert(this.mnemonic.Get(false) == mnemonic_suffix.Get(false));
				mnemonic = this.mnemonic;
			}
			else {
				if (bitness != instrBitness || options.GasShowMnemonicSizeSuffix)
					mnemonic = mnemonic_suffix;
				else
					mnemonic = this.mnemonic;
			}
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_bnd2 : InstrInfo {
		readonly FormatterString mnemonic;
		readonly FormatterString mnemonic_suffix;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_bnd2(string mnemonic, string mnemonic_suffix) : this(mnemonic, mnemonic_suffix, InstrOpInfoFlags.None) { }

		public SimpleInstrInfo_bnd2(string mnemonic, string mnemonic_suffix, InstrOpInfoFlags flags) {
			this.mnemonic = new FormatterString(mnemonic);
			this.mnemonic_suffix = new FormatterString(mnemonic_suffix);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = this.flags;
			if (instruction.HasRepnePrefix)
				flags |= InstrOpInfoFlags.BndPrefix;
			info = new InstrOpInfo(GetMnemonic(options, instruction, mnemonic, mnemonic_suffix, flags), instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_pops : InstrInfo {
		readonly FormatterString mnemonic;
		readonly FormatterString[] pseudo_ops;

		public SimpleInstrInfo_pops(string mnemonic, FormatterString[] pseudo_ops) {
			this.mnemonic = new FormatterString(mnemonic);
			this.pseudo_ops = pseudo_ops;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
			int imm = instruction.Immediate8;
			if (options.UsePseudoOps && (uint)imm < (uint)pseudo_ops.Length) {
				RemoveFirstImm8Operand(ref info);
				info.Mnemonic = pseudo_ops[imm];
			}
		}

		internal static void RemoveFirstImm8Operand(ref InstrOpInfo info) {
			Debug.Assert(info.Op0Kind == InstrOpKind.Immediate8);
			info.OpCount--;
			switch (info.OpCount) {
			case 0:
				info.Op0Index = OpAccess_INVALID;
				break;

			case 1:
				info.Op0Kind = info.Op1Kind;
				info.Op0Register = info.Op1Register;
				info.Op0Index = info.Op1Index;
				info.Op1Index = OpAccess_INVALID;
				break;

			case 2:
				info.Op0Kind = info.Op1Kind;
				info.Op0Register = info.Op1Register;
				info.Op1Kind = info.Op2Kind;
				info.Op1Register = info.Op2Register;
				info.Op0Index = info.Op1Index;
				info.Op1Index = info.Op2Index;
				info.Op2Index = OpAccess_INVALID;
				break;

			case 3:
				info.Op0Kind = info.Op1Kind;
				info.Op0Register = info.Op1Register;
				info.Op1Kind = info.Op2Kind;
				info.Op1Register = info.Op2Register;
				info.Op2Kind = info.Op3Kind;
				info.Op2Register = info.Op3Register;
				info.Op0Index = info.Op1Index;
				info.Op1Index = info.Op2Index;
				info.Op2Index = info.Op3Index;
				info.Op3Index = OpAccess_INVALID;
				break;

			case 4:
				info.Op0Kind = info.Op1Kind;
				info.Op0Register = info.Op1Register;
				info.Op1Kind = info.Op2Kind;
				info.Op1Register = info.Op2Register;
				info.Op2Kind = info.Op3Kind;
				info.Op2Register = info.Op3Register;
				info.Op3Kind = info.Op4Kind;
				info.Op3Register = info.Op4Register;
				info.Op0Index = info.Op1Index;
				info.Op1Index = info.Op2Index;
				info.Op2Index = info.Op3Index;
				info.Op3Index = info.Op4Index;
				info.Op4Index = OpAccess_INVALID;
				break;

			default:
				throw new InvalidOperationException();
			}
		}
	}

	sealed class SimpleInstrInfo_sae_pops : InstrInfo {
		readonly int saeIndex;
		readonly FormatterString mnemonic;
		readonly FormatterString[] pseudo_ops;

		public SimpleInstrInfo_sae_pops(int saeIndex, string mnemonic, FormatterString[] pseudo_ops) {
			this.saeIndex = saeIndex;
			this.mnemonic = new FormatterString(mnemonic);
			this.pseudo_ops = pseudo_ops;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
			if (instruction.SuppressAllExceptions)
				SimpleInstrInfo_er.MoveOperands(ref info, saeIndex, InstrOpKind.Sae);
			int imm = instruction.Immediate8;
			if (options.UsePseudoOps && (uint)imm < (uint)pseudo_ops.Length) {
				SimpleInstrInfo_pops.RemoveFirstImm8Operand(ref info);
				info.Mnemonic = pseudo_ops[imm];
			}
		}
	}

	sealed class SimpleInstrInfo_pclmulqdq : InstrInfo {
		readonly FormatterString mnemonic;
		readonly FormatterString[] pseudo_ops;

		public SimpleInstrInfo_pclmulqdq(string mnemonic, FormatterString[] pseudo_ops) {
			this.mnemonic = new FormatterString(mnemonic);
			this.pseudo_ops = pseudo_ops;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
			if (options.UsePseudoOps) {
				int index;
				int imm = instruction.Immediate8;
				if (imm == 0)
					index = 0;
				else if (imm == 1)
					index = 1;
				else if (imm == 0x10)
					index = 2;
				else if (imm == 0x11)
					index = 3;
				else
					index = -1;
				if (index >= 0) {
					SimpleInstrInfo_pops.RemoveFirstImm8Operand(ref info);
					info.Mnemonic = pseudo_ops[index];
				}
			}
		}
	}

	sealed class SimpleInstrInfo_imul : InstrInfo {
		readonly FormatterString mnemonic;
		readonly FormatterString mnemonic_suffix;

		public SimpleInstrInfo_imul(string mnemonic, string mnemonic_suffix) {
			this.mnemonic = new FormatterString(mnemonic);
			this.mnemonic_suffix = new FormatterString(mnemonic_suffix);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			const InstrOpInfoFlags flags = 0;
			info = new InstrOpInfo(GetMnemonic(options, instruction, mnemonic, mnemonic_suffix, flags), instruction, flags);
			Debug.Assert(info.OpCount == 3);
			if (options.UsePseudoOps && info.Op1Kind == InstrOpKind.Register && info.Op2Kind == InstrOpKind.Register && info.Op1Register == info.Op2Register) {
				info.OpCount--;
				info.Op1Index = OpAccess_ReadWrite;
				info.Op2Index = OpAccess_INVALID;
			}
		}
	}

	sealed class SimpleInstrInfo_Reg16 : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_Reg16(string mnemonic) {
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			const InstrOpInfoFlags flags = InstrOpInfoFlags.None;
			info = new InstrOpInfo(mnemonic, instruction, flags);
			if (Register.EAX <= (Register)info.Op0Register && (Register)info.Op0Register <= Register.R15D) {
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)((Register)info.Op0Register - Register.EAX + Register.AX);
			}
			if (Register.EAX <= (Register)info.Op1Register && (Register)info.Op1Register <= Register.R15D) {
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op1Register = (byte)((Register)info.Op1Register - Register.EAX + Register.AX);
			}
		}
	}

	sealed class SimpleInstrInfo_DeclareData : InstrInfo {
		readonly FormatterString mnemonic;
		readonly InstrOpKind opKind;

		public SimpleInstrInfo_DeclareData(Code code, string mnemonic) {
			this.mnemonic = new FormatterString(mnemonic);
			var opKind = code switch {
				Code.DeclareByte => InstrOpKind.DeclareByte,
				Code.DeclareWord => InstrOpKind.DeclareWord,
				Code.DeclareDword => InstrOpKind.DeclareDword,
				Code.DeclareQword => InstrOpKind.DeclareQword,
				_ => throw new InvalidOperationException(),
			};
			this.opKind = opKind;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.KeepOperandOrder | InstrOpInfoFlags.MnemonicIsDirective);
			info.OpCount = (byte)instruction.DeclareDataCount;
			info.Op0Kind = opKind;
			info.Op1Kind = opKind;
			info.Op2Kind = opKind;
			info.Op3Kind = opKind;
			info.Op4Kind = opKind;
			info.Op0Index = OpAccess_Read;
			info.Op1Index = OpAccess_Read;
			info.Op2Index = OpAccess_Read;
			info.Op3Index = OpAccess_Read;
			info.Op4Index = OpAccess_Read;
		}
	}
}
#endif
